/*
       _             __
  ____(_)__ _______ / /____ ____
 / __/ (_-</ __(_-</ __/ -_) __/
/_/ /_/___/\__/___/\__/\__/_/

Napster client for RISC OS
Copyright (C) 2000 Robert Dimond

Portions are based on gnap by Ryan Dahl.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA

Bransley Cottage, Cleobury Mortimer, Kidderminster, WORCS. DY14 0BZ
England. robdimond@cwcom.net

*/

#include "runimage.h"
#include "dns.h"

#define BUFFSIZE 0xffff

socket_s server;
socket_s redirserver;
socket_s listen;

char buffer[0xffff];

/* why oh why did I do it like this!!! */

extern char email[];
extern char username[];
extern char password[];
extern int connection;
extern int new_user;
extern connect_level connected;
extern search_result * search_resblock;
extern int search_currentres;
extern int listening_port;
extern transfer downloads[];
extern incoming incon[];
extern search_result browsed_user;
extern char altserver_name[];
extern int altserver_port;

char ip_bestserver[34];
char debugmsg[200];


int on=1;


message_status mess_stat=get_head;
int mess_recvd=0, mess_len=0, mess_command=0;
char mess_header[4];

struct in_addr ip_redir;

void napster_resolve_redir(void)
{
 connectedchange(nap_res);
 switch(dns_find_ip_address(HOSTNAME, &ip_redir))
 {
  case socket_EINPROGRESS: return; break;
  case 0:
  napster_connect(); break;
  default:
  ui_messalert("ERRCNRS");
  napster_close();
  break;
 }
}

void napster_resolve_alt(void)
{
 connectedchange(alt_res);
 switch(dns_find_ip_address(altserver_name, &ip_redir))
 {
  case socket_EINPROGRESS: return; break;
  case 0:
  napster_serverconnect(ip_redir.s_addr, portconv(altserver_port)); break;
  default:
  ui_messalert("ERRCNRSA");
  napster_close();
  break;
 }
}

int napster_connect(void)
{
 int b;
 socket_sockaddr serveraddress;
 mess_stat=get_head;

 if(error(xsocket_creat(socket_AF_INET,
                  socket_SOCK_STREAM,
                  socket_IPPROTO_IP, &redirserver))) return(-1);


 if(error(xsocket_ioctl(redirserver, socket_FIONBIO, (byte *) &on)))
 {
  xsocket_close(redirserver);
  return(-2);
 }
 /* connect socket to remote server */
 serveraddress.sockaddr_in.af=socket_AF_INET;
 serveraddress.sockaddr_in.port=portconv(HOSTPORT);
 serveraddress.sockaddr_in.addr=/*ipconv(HOSTIP)*/ip_redir.s_addr;
 for(b=0; b<8; b++) serveraddress.sockaddr_in.data[b]=0;
 connectedchange(tc_redir);
 motd_addline("Connecting to redirect server...");
 strcpy(ip_bestserver, "");
 if(nerror(xsocket_connect(redirserver, &serveraddress, sizeof(serveraddress))))
 {
  napster_close();
  return(-3);
 }
 return(0);
}

int napster_serverconnect(unsigned int ip, int port)
{
 int b;
 socket_sockaddr serveraddress;
 mess_stat=get_head;

 if(error(xsocket_creat(socket_AF_INET,
                  socket_SOCK_STREAM,
                  socket_IPPROTO_IP, &server))) return(-1);


 if(error(xsocket_ioctl(server, socket_FIONBIO, (byte *) &on)))
 {
  xsocket_close(server);
  return(-2);
 }
 /* connect socket to remote server */
 serveraddress.sockaddr_in.af=socket_AF_INET;
 serveraddress.sockaddr_in.port=port;
 serveraddress.sockaddr_in.addr=ip;
 for(b=0; b<8; b++) serveraddress.sockaddr_in.data[b]=0;
 connectedchange(tc_server);
 motd_addline("Connecting to server...");
 if(nerror(xsocket_connect(server, &serveraddress, sizeof(serveraddress))))
 {
  napster_close();
  return(-3);
 }
 return(0);
}

int nerror(os_error * err)
{
 int e;
 if (err!=NULL)
 {
  e=err->errnum;
  if ((e==socket_EAGAIN)||(e==socket_EWOULDBLOCK)||
      (e==socket_EINPROGRESS))
     {
      return (0);
     }
     else
     {
      ui_alert(err->errmess);
      return(1);
     }
 }
}

void napster_handle_bestserver()
{
 char c;
 int cc=0, pos, b;
 unsigned int newip;
 int newport;

 if (nerror(xsocket_recv(redirserver, (byte *) &c, 1, 0, &cc))) return;
 pos=strlen(ip_bestserver);
 if (cc)
 {
  ip_bestserver[pos]=c;
  ip_bestserver[pos+1]='\0';
 }
 /*if (strlen(ip_bestserver)>=20)*/
 if(ip_bestserver[strlen(ip_bestserver)-5]==':')
 {
  if(strstr(ip_bestserver, "127.0.0.1"))
  {
   ui_messalert("ERRBUSY");
   napster_close();
  }
  else
  {
   nerror(xsocket_close(redirserver));
   sprintf(debugmsg, "Redirecting to: %s", ip_bestserver);
   napster_get_best_host(&newip, &newport, ip_bestserver);
   motd_addline(debugmsg);
   napster_serverconnect(newip, newport);
  }
 }
}

void napster_close()
{
 switch(connected)
 {
  case notc:
  case nap_res:
  case alt_res:
  break;
  case tc_redir:
  case get_redir:
  nerror(xsocket_close(redirserver));
  break;
  case tc_server:
  case try_login:
  case loggedin:
  nerror(xsocket_close(server));
  break;
 }
 connectedchange(notc);
}

int napster_mo_userlogin(char * username, char * password, int port, int connection, int new_user)
{
 char connect_string[200]="";
 if (!new_user)
 {
  sprintf(connect_string,"%s %s %i \"%s\" %i",username,password,port,VERSION_STRING,connection);

  napster_send(connect_string, 2, strlen(connect_string));


 }
 else
 {
  sprintf(connect_string,"%s %s %i \"%s\" %i %s",username,password,port,VERSION_STRING,connection, email);
  napster_send(connect_string, 6, strlen(connect_string));
 }
 return(0);
}

int napster_mo_newuser(char * username)
{
 napster_send(username, 7, strlen(username));
 return(0);
}


int napster_mo_dlrequest(search_result * toget)
{
 char get_string[300];
 sprintf(get_string, "%s \"%s\"", toget->user, toget->filename);
 napster_send(get_string, 203, strlen(get_string));
 return(0);
}

void napster_get_best_host(unsigned int * ipo, int * porto, char * string)
{
 char * ip, * port, * c;
 ip=string;
 port=strchr(string, ':');
 port[0]='\0';
 port+=sizeof(char);
 c=strchr(port, '\0');
 c[0]='\0';
 *porto=portconv(atoi(port));
 *ipo=ipconv(ip);
}

int portconv(int port)
{
 char *b, *c;
 b=(char *)&port;
 c=b+1;
 return(*c|(*b<<8));
}

int ipconv(char * ipin)
{
 char c[]=".";
 char ip[]="000.000.000.000";
 char * t;
 int out=0, n=0;
 strcpy(ip, ipin);
 t=strtok(ip, c);
 while ((t!=NULL)&&(n<4))
 {
  out+=atoi(t)<<(n*8);
  t=strtok(NULL, c);
  n++;
 }
 /*os_writec('\4');
 printf("%s %d\n", ip, out);*/
 return(out);
}

int napster_poll(void)
{
 int i;
 socket_s newsocket;
 os_error * sele;
 socket_timeval timeout;
 long readfd=0, writefd=0;
 int nfound=0;

 readfd=readfd|(long)1<<(int)listen;
 switch(connected)
 {
  case notc:
  break;
  case nap_res:
  break;
  case tc_redir:
  writefd=writefd|(long)1<<(int)redirserver;
  break;
  case get_redir:
  readfd=readfd|(long)1<<(int)redirserver;
  break;
  case tc_server:
  writefd=writefd|(long)1<<(int)server;
  break;
  default:
  if (connected>=try_login)
  {
   readfd=readfd|(long)1<<(int)server;
   for(i=0; i<C_DOWNLOADS; i++)
   {
    if(downloads[i].active==tc) writefd=writefd|(long)1<<(int)downloads[i].socket;
    if(downloads[i].active>=get_one)
                               readfd=readfd|(long)1<<(int)downloads[i].socket;
   }
  }
 }

 for(i=0; i<C_INCOMING; i++)
 {
  if(incon[i].active) readfd=readfd|(long)1<<(int)incon[i].socket;
 }

 timeout.sec=0; timeout.usec=0;
 sele=xsocket_select(32, (socket_fdset *) &readfd,
                          (socket_fdset *) &writefd,
                               NULL, &timeout, &nfound);
 if((sele==NULL)&&((connected<tc_redir)||(nfound)))
 {
  if(readfd&((long)1<<(int)listen))
  {
  if(!nerror(xsocket_accept(listen, NULL, NULL, &newsocket)))
                                             napster_incoming(newsocket);
  }
  switch(connected)
  {
   case notc:
   break;
   case nap_res:
   napster_resolve_redir();
   break;
   case alt_res:
   napster_resolve_alt();
   break;
   case tc_redir:
   if(writefd&((long)1<<(int)redirserver)) connectedchange(get_redir);
   break;
   case get_redir:
   if(readfd&((long)1<<(int)redirserver)) napster_handle_bestserver();
   break;
   case tc_server:
   if(writefd&((long)1<<(int)server))
   {
    motd_addline("Connected, trying to log in...");
    if(!new_user)
    napster_mo_userlogin(username, password, listening_port, connection, 0);
    else napster_mo_newuser(username);
    connectedchange(try_login);
   }
   break;
   default:
   if(connected>=try_login)
   {
    if(readfd&((long)1<<(int)server))
    {
     napster_handle_input();
    }
    for(i=0; i<C_DOWNLOADS; i++)
    {
     if((downloads[i].active==tc)&&(writefd&((long)1<<(int)downloads[i].socket)))
       napster_handle_nfconnected(i);
     if((downloads[i].active>=get_one)&&(readfd&((long)1<<(int)downloads[i].socket)))
     {

      switch(downloads[i].active)
      {
       case get_one:
       napster_handle_one(i);
       break;
       case get_header:
       napster_handle_header(i);
       break;
       case download:
       napster_handle_download(i);
       break;
       default: break;
      }
     }
    }
   }
   break;
  }
  for(i=0; i<C_INCOMING; i++)
  {
   if((incon[i].active)&&(readfd&((long)1<<(int)incon[i].socket)))
   {
    napster_handle_inconheader(i);
   }
  }
 }
 else
 {
  switch(sele->errnum)
  {
   case socket_EBADF:
   /* pretty much fatal so we have to close the connection */
   napster_close();
   break;
   default:
   nerror(sele);
   break;
  }
 }
 return(nfound);
}

void napster_incoming(socket_s newsocket)
{
 int cc=0;
 int in_slot=0;
 while((in_slot<=C_INCOMING)&&(incon[in_slot].active!=0))
 {
  in_slot++;
 }
 if(incon[in_slot].active!=0)
 {
  xsocket_close(newsocket);
  return;
 }
 if (nerror(xsocket_send(newsocket, (byte *) "1", 1, 0, &cc)))
 {
  ui_messalert("ERRCOMC");
  xsocket_close(newsocket);
  return;
 }
 incon[in_slot].socket=newsocket;
 incon[in_slot].active=1;
 incon[in_slot].hpos=0;
 incon[in_slot].header[0]='\0';
 incon[in_slot].command=0;
}

void napster_handle_inconheader(int ino)
{
 char c;
 int cc=0, i;
 if (nerror(xsocket_recv(incon[ino].socket, (byte *) &c, 1, 0, &cc)))
 {
  ui_messalert("ERRCOMC");
  incon[ino].active=0;
  xsocket_close(incon[ino].socket);
  return;
 }
 incon[ino].header[incon[ino].hpos]=c;
 incon[ino].hpos++;
 incon[ino].header[incon[ino].hpos]='\0';
 if(incon[ino].active==1)
 {
  if(!strcmp(incon[ino].header, "SEND"))
  {
   incon[ino].command=1;
   incon[ino].hpos=0;
   incon[ino].active=2;
   return;
  }
 }
 if(incon[ino].active==2)
 {
  if (c=='\"')
  {
   incon[ino].active=3;
   return;
  }
 }
 if(incon[ino].active==3)
 {
  if (c==' ')
  {
   for(i=0; i<C_DOWNLOADS; i++)
   {
    if((downloads[i].active==getinfo)&&
       (strstr(incon[ino].header, downloads[i].filename)))
    {
     cc=0;
     if(nerror(xsocket_send(incon[ino].socket, (byte *) "0", 1, 0, &cc)))
     {
      incon[ino].active=0;
      xsocket_close(incon[ino].socket);
      ui_messalert("ERRCOMC");
      return;
     }
     if(cc)
     {
      incon[ino].active=0;
      downloads[i].socket=incon[ino].socket;
      downloads[i].active=get_header;
      downloads[i].headcount=0;
     }
    }
   }
  }
 }
}

void napster_handle_header(int tno)
{
 int cc;
 char c;
 if(nerror(xsocket_recv(downloads[tno].socket, (byte *) &c, 1,
                                              socket_MSG_PEEK, &cc)))
 {
  ui_messalert("ERRCOMC");
  napster_download_end(tno);
 }

 if (!((c>='0')&&(c<='9')))
 {
  downloads[tno].header[downloads[tno].headcount]='\0';
  //sprintf(debugmsg, "Got size header:  %s bytes", downloads[tno].header);
  //motd_addline(debugmsg);
  napster_send((char *) "", 218, 0);
  downloads[tno].totalsize=atoi((downloads[tno].header));
  downloads[tno].active=download;
  search_updatebar(tno);
 }
 else
 {
  if(nerror(xsocket_recv(downloads[tno].socket, (byte *) &c, 1, 0, &cc)))
  {
   ui_messalert("ERRCOMC");
   napster_download_end(tno);
  }
  downloads[tno].header[downloads[tno].headcount]=c;
  downloads[tno].headcount++;
 }
}

void napster_handle_download(int tno)
{
 char buffer[BUFFSIZE];
 int amount_recv, i;
 //for (i=0; i<2048; i++) buffer[i]=0;
 /*amount_recv = recv(t_current.socket,buffer,2048,0);*/
 nerror(xsocket_recv(downloads[tno].socket, (byte *)buffer,
                                               BUFFSIZE, 0, &amount_recv));
 fwrite(buffer, sizeof(char), amount_recv, downloads[tno].filehandle);
 downloads[tno].size += amount_recv;
 if (downloads[tno].size>=downloads[tno].totalsize) napster_download_end(tno);
}

void napster_download_end(int tno)
{
 if (downloads[tno].active==download)
 {
  napster_send((char *) "", 219, 0);
 }
 if (downloads[tno].active>=tc)
 {
  error(xsocket_close(downloads[tno].socket));
 }
 if ((downloads[tno].active>=window)&&(downloads[tno].filehandle!=NULL))
 {
  if(downloads[tno].size<downloads[tno].totalsize)
                                   lib_writefooter(&downloads[tno]);
  if (fclose(downloads[tno].filehandle)==EOF)
  {
   ui_messalert("ERRCSC");
  }
 }
 get_closewin(tno);
 downloads[tno].active=freesl;
}

void napster_send(char * data, int type, int length)
{
 char msb, lsb;
 char head[4];
 //char motd[255];
 int cc;
 msb=(length>>8);
 lsb=length-(msb<<8);
 head[0]=lsb; head[1]=msb;
 msb=(type>>8);
 lsb=type-(msb<<8);
 head[2]=lsb; head[3]=msb;
 if((nerror(xsocket_send(server, (byte *)head, 4, 0, &cc)))
     ||(nerror(xsocket_send(server, (byte *)data, length, 0, &cc))))
 {
  ui_messalert("ERRCOMS");
  napster_close();
  return;
 }
 //sprintf(motd, "> Type:%d Len:%d  %s", type, length, data);
 //motd_addline(motd);
}

void napster_mess_loginerror(char * message)
{
 /*printf("Error from server: %s\n", message);*/
 ui_alert(message);
 napster_close();
}

void napster_mess_error(char * message)
{
 ui_alert(message);
 search_ungrey();
}

void napster_mess_loginack(char * email)
{
 /*printf("Logged in, email is %s\n", email);*/
 ui_messalert("SMLOGIN");
/* napster_init_server();*/
 connectedchange(loggedin);
}

void napster_mess_regsuccess(void)
{
 /*printf("New user registration success\n");*/
 ui_messalert("SMREGS");
 napster_mo_userlogin(username, password, 6699, connection, 1);
}

void napster_mess_alreadyreg(void)
{
 /*printf("Nickname is already in use by another user\n");*/
 ui_messalert("SMNICK");
 napster_close();
}

void napster_mess_invalidreg(void)
{
 /*printf("Nickname is invalid\n");*/
 ui_messalert("SMINV");
 napster_close();
}

void napster_mess_searchresponse(char * line)
{
 /*"<filename>" <md5> <size> <bitrate> <frequency>
                              <length> <nick> <ip> <link-type>*/
 search_result * result;
 char *filename,user[40],id[100];
 int number,size,bitrate,frequency,speed;
 unsigned int ip;
 filename = line;
 filename ++;
 line = (char*)strchr(filename,'\"');
 if(!line) {
 /* printf("strange filename error\n");
  os_writec('\4');
  printf("%s\n", line);  */
  ui_messalert("ERRSFILE");
  return;
 }
 line[0] = '\0';
 line ++;
 result = malloc(sizeof(search_result));
 if (result==NULL) return;
 sscanf(line," %s %u %d %d %d %s %u %d",id,&size,&bitrate,&frequency,&number,user,&ip,&speed);

 strcpy(result->filename, filename);
 strcpy(result->id, id);
 result->number = number;
 result->ip = ip;
 strcpy(result->user, user);
 result->size = size;
 result->bitrate = bitrate;
 result->frequency = frequency;
 result->speed = speed;
 result->type = search;
 result->next = NULL;
 if (speed<0) return;
 search_update(result);
}

void napster_mess_browseresponse(char * line)
{
 /* <nick> "<filename>" <md5> <size> <bitrate> <frequency> <time> */
 char * end, * rest;
 search_result * result;
 result=malloc(sizeof(search_result));
 end = strchr(line, (int) '\"');
 *(end-1)='\0';
 strcpy(result->user, line);
 rest=strchr(end+1, (int) '\"');
 *(rest)='\0';
 strcpy(result->filename, end+1);
 sscanf(rest+1," %s %u %d %d %d ", result->id, &result->size,
                                   &result->bitrate, &result->frequency,
                                   &result->number);
 result->next=NULL;
 result->type=browse;
 result->speed=browsed_user.speed;
 result->ip=browsed_user.ip;
 search_update(result);
}

void napster_mess_completeresponse(char * line)
{
 os_writec('\4');
 printf("%s\n", line);
}

void napster_mess_searchend()
{
 search_finish();
}

void napster_mess_dlack(char * buffer)
{
 /*char motdt[100];*/
 char getstring[300];
 char fn[300], fnt[255];
 char *filename,*rest,user[40],id[40];
 int port,speed;
 unsigned int ip;
 int tno=-1, i;
 int b, used=0;
 char dot[]=".";
 socket_sockaddr otheruser;
 filename = strchr(buffer,'\"');
 filename[0] = '\0';
 filename ++;
 rest = strchr(filename,'\"');
 rest[0] = '\0';
 rest++;
 sscanf(buffer,"%s %u %d ",user,&ip,&port);
 sscanf(rest," %s %d",id,&speed);
 //sprintf(debugmsg, "Connect to port %d", port);
 //motd_addline(debugmsg);

 if(error(xos_read_var_val("Riscster$MusicDir", fn, 300, 0, os_VARTYPE_STRING,
                                                          &used, NULL, NULL)))
 {
  ui_messalert("ERRMPNS");
  return;
 }
 fn[used]='\0';
 for (i=0; i<C_DOWNLOADS; i++)
 {
  if (downloads[i].active==getinfo)
  {
   if (strcmp(downloads[i].filename, filename)==0) tno=i;
  }
 }
 if (tno==-1)
 {
  return;
 }
 strcpy(fnt, filename);
 strcat(fn, dot);
 strcat(fn, napster_sanefile(fnt));
 /*os_writec('\4');
 printf("%s\n", fn);*/
 downloads[tno].filehandle=fopen(fn, "wb");
 if (downloads[tno].filehandle==NULL)
 {
  ui_messalert("ERRSCRAP");
  napster_download_end(tno);
  return;
 }
 error(xosfile_set_type(fn, 0x1ad));
 /*downloads[tno].totalsize=(search_resblock+search_currentres)->size;*/

 if (port==0)
 {
  sprintf(getstring, "%s \"%s\" 0", downloads[tno].user, filename);
  napster_send(getstring, 500, strlen(getstring));
  return;
 }
 if(error(xsocket_creat(socket_AF_INET, socket_SOCK_STREAM, socket_IPPROTO_IP,
                                                 &downloads[tno].socket)))
 {
  ui_messalert("ERRCNCN");
  napster_download_end(tno);
  return;
 }
 error(xsocket_ioctl(downloads[tno].socket, socket_FIONBIO, (byte *) &on));
 otheruser.sockaddr_in.af=socket_AF_INET;
 otheruser.sockaddr_in.port=portconv(port);
 otheruser.sockaddr_in.addr=ip;
 for(b=0; b<8; b++) otheruser.sockaddr_in.data[b]=0;

 if(nerror(xsocket_connect(downloads[tno].socket, &otheruser, sizeof(otheruser))))
 {
  ui_messalert("ERRCNCN");
  /*xsocket_close(downloads[tno].socket);*/
  napster_download_end(tno);
  return;
 }
 downloads[tno].active=tc;
 downloads[tno].headcount=0;
}

void napster_handle_nfconnected(int tno)
{
 char getstring[300];
 int cc=0;
 nerror(xsocket_send(downloads[tno].socket, (byte *) "GET", 3, 0, &cc));
 sprintf(getstring, "%s \"%s\" 0", username, downloads[tno].filename);
 cc=0;
 nerror(xsocket_send(downloads[tno].socket,
                            (byte *) getstring, strlen(getstring), 0, &cc));
 downloads[tno].active=get_one;
 search_updatebar(tno);
}

void napster_handle_one(int tno)
{
 int cc=0;
 char mustbeone;
 nerror(xsocket_recv(downloads[tno].socket, (byte *) &mustbeone, 1, 0, &cc));
 if (mustbeone!='1')
 {
  ui_messalert("ERRIHE");
  napster_download_end(tno);
 }
 downloads[tno].headcount=0;
 downloads[tno].active=get_header;
 search_updatebar(tno);
}

char * napster_sanefile(char * file)
{
 char * nfile;
 nfile=strrchr(file, (int) '\\');
 if (nfile!=NULL) file=nfile+1;
 nfile=file;
 while (*nfile!='\0')
 {
  /*if (*nfile=='.') *nfile='/';
  if (*nfile==' ') *nfile='_';*/
  switch(*nfile)
  {
   case '.': *nfile='/'; break;
   case ' ': *nfile='_'; break;
   case ':': *nfile='-'; break;
   case '*': *nfile='x'; break;
   case '#': *nfile='H'; break;
   case '$': *nfile='S'; break;
   case '&': *nfile='+'; break;
   case '@': *nfile='a'; break;
   case '^': *nfile='\''; break;
   case '%': *nfile='/'; break;
   case '\\': *nfile='/'; break;
   default: break;
  }
  nfile++;
 }
 return(file);
}

void napster_mess_motd(char * buffer)
{
 /*os_writec('\4');
 printf("%s\n", buffer);*/
 motd_addline(buffer);
}

void napster_handle_input(void)
{
 char buffer2[0xffff];
 switch(mess_stat)
 {
  case get_head:
  {
   int cc=0, i=0;
   FILE * messydebug;
   if(nerror(xsocket_recv(server, (byte *) &mess_header[mess_recvd],
                                        1, 0, &cc)))
   {
    ui_messalert("ERRCOMS");
    napster_close();
    return;
   }

   mess_recvd+=cc;
   if(mess_recvd==4)
   {
    mess_len=mess_header[0]|(mess_header[1]<<8);
    mess_command=mess_header[2]|(mess_header[3]<<8);
    for(i=0; i<sizeof(buffer); i++) buffer[i]='\0';
    if (mess_len>0) mess_stat=get_data;
      else napster_handle_message(mess_command, buffer);
    mess_recvd=0;
    /*messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
    fprintf(messydebug, "Length: %d  Type: %d\n", mess_len, mess_command);
    fclose(messydebug);*/
   }
  }
  break;
  case get_data:
  {
   int cc=0, i=0;
   /*FILE * messydebug;
   messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
   fprintf(messydebug, "Got Already: %d\n", mess_recvd);
   fclose(messydebug);*/
   for (i=0; i<sizeof(buffer2); i++) buffer2[i]='\0';
   if(nerror(xsocket_recv(server, (byte *) buffer2,
                            (mess_len-mess_recvd), 0, &cc)))
   {
    ui_messalert("ERRCOMS");
    napster_close();
   }
   /*messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
   fprintf(messydebug, "Just Got: %d\n", cc);
   fclose(messydebug);*/
   memcpy(buffer+mess_recvd, buffer2, cc);
   mess_recvd+=cc;

   if(mess_recvd==mess_len)
   {
    napster_handle_message(mess_command, buffer);
    mess_stat=get_head;
    mess_len=0;
    mess_command=0;
    mess_recvd=0;
   }
  }
  break;
 }
}

void napster_handle_message(int mc, char * buffer)
{
 #ifdef DEBUG
  FILE * messydebug;
  messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
  fprintf(messydebug, "%d: %s\n", mc, buffer);
  fclose(messydebug);
 #endif
 switch(mc)
 {
  case 0:
  napster_mess_loginerror(buffer); break;
  case 3:
  napster_mess_loginack(buffer); break;
  case 8:
  napster_mess_regsuccess(); break;
  case 9:
  napster_mess_alreadyreg(); break;
  case 10:
  napster_mess_invalidreg(); break;
  case 201:
  napster_mess_searchresponse(buffer); break;
  case 202:
  case 213:
  napster_mess_searchend(); break;
  case 205:
  napster_mess_privatemessage(buffer); break;
  case 212:
  napster_mess_browseresponse(buffer); break;
  case 204:
  napster_mess_dlack(buffer); break;
  case 216:
  napster_mess_completeresponse(buffer); break;
  case 217:
  /*
  os_writec('\4');
  printf("Done!\n"); break;
  */
  case 404:
  napster_mess_error(buffer); break;
  case 621:
  napster_mess_motd(buffer); break;
  case 751:
  napster_send("", 752, 0); /*ping response (pong)*/
  break;
 }
}

int napster_init_server()
{
 socket_sockaddr localport;
 int b;
 int tryport=6698;
 if(nerror(xsocket_creat(socket_AF_INET,
                  socket_SOCK_STREAM,
                  socket_IPPROTO_IP, &listen))) return(-1);
 nerror(xsocket_ioctl(listen, socket_FIONBIO, (byte *) &on));
 do
 {
  tryport++;
  localport.sockaddr_in.af=socket_AF_INET;
  localport.sockaddr_in.port=portconv(tryport);
  localport.sockaddr_in.addr=0;
  for(b=0; b<8; b++) localport.sockaddr_in.data[b]=0;

 }while(xsocket_bind(listen, &localport, sizeof(localport))&&
                                                     (tryport<6800));
 listening_port=tryport;
 if(nerror(xsocket_listen(listen, 5))) return(-2);
 return(0);
}

int napster_end_server()
{
 return(nerror(xsocket_close(listen)));
}

void napster_mess_privatemessage(char * buffer)
{
 //os_writec('\4');
 //printf("%s", buffer);
 motd_addline("Private message received:");
 motd_addline(buffer);
}



